//! Utils built-in plugin
//! 
//! This plugin provides utility functions for test execution.

use super::BuiltinPlugin;
use crate::error::{Error, Result};
use crate::models::{Value, ValueMap};
use crate::utils::builtin::BuiltinFunctions;
use serde_json;
use std::collections::HashMap;

/// Utils built-in plugin
#[derive(Debug)]
pub struct UtilsPlugin {
    /// Plugin configuration
    config: ValueMap,
}

impl UtilsPlugin {
    /// Create a new utils plugin
    pub fn new() -> Self {
        Self {
            config: HashMap::new(),
        }
    }
    
    /// Get current timestamp
    async fn now(&self, _args: &[Value]) -> Result<Value> {
        Ok(serde_json::json!(BuiltinFunctions::now()))
    }
    
    /// Generate random string
    async fn random_string(&self, args: &[Value]) -> Result<Value> {
        let length = if args.is_empty() {
            10
        } else {
            args[0].as_u64().unwrap_or(10) as usize
        };
        
        Ok(serde_json::json!(BuiltinFunctions::random_string(length)))
    }
    
    /// Generate UUID
    async fn uuid(&self, _args: &[Value]) -> Result<Value> {
        Ok(serde_json::json!(uuid::Uuid::new_v4().to_string()))
    }
    
    /// Sleep for specified duration
    async fn sleep(&self, args: &[Value]) -> Result<Value> {
        if args.is_empty() {
            return Ok(serde_json::json!({"status": "no_duration"}));
        }
        
        let duration_ms = args[0].as_u64().unwrap_or(1000);
        tokio::time::sleep(tokio::time::Duration::from_millis(duration_ms)).await;
        
        Ok(serde_json::json!({
            "status": "slept",
            "duration_ms": duration_ms
        }))
    }
    
    /// Base64 encode
    async fn base64_encode(&self, args: &[Value]) -> Result<Value> {
        if args.is_empty() {
            return Err(Error::Internal("Missing input for base64 encoding".to_string()));
        }
        
        let input = args[0].as_str().unwrap_or("");
        use base64::{Engine as _, engine::general_purpose};
        let encoded = general_purpose::STANDARD.encode(input);
        
        Ok(serde_json::json!(encoded))
    }
    
    /// Base64 decode
    async fn base64_decode(&self, args: &[Value]) -> Result<Value> {
        if args.is_empty() {
            return Err(Error::Internal("Missing input for base64 decoding".to_string()));
        }
        
        let input = args[0].as_str().unwrap_or("");
        use base64::{Engine as _, engine::general_purpose};
        match general_purpose::STANDARD.decode(input) {
            Ok(decoded) => {
                let decoded_str = String::from_utf8_lossy(&decoded);
                Ok(serde_json::json!(decoded_str))
            }
            Err(e) => Err(Error::Internal(format!("Base64 decode error: {}", e))),
        }
    }
    
    /// MD5 hash
    async fn md5(&self, args: &[Value]) -> Result<Value> {
        if args.is_empty() {
            return Err(Error::Internal("Missing input for MD5 hash".to_string()));
        }
        
        let input = args[0].as_str().unwrap_or("");
        let hash = format!("{:x}", md5::compute(input));
        
        Ok(serde_json::json!(hash))
    }
    
    /// SHA256 hash
    async fn sha256(&self, args: &[Value]) -> Result<Value> {
        if args.is_empty() {
            return Err(Error::Internal("Missing input for SHA256 hash".to_string()));
        }
        
        let input = args[0].as_str().unwrap_or("");
        use sha2::{Sha256, Digest};
        let mut hasher = Sha256::new();
        hasher.update(input.as_bytes());
        let hash = hasher.finalize();
        let hash_str = format!("{:x}", hash);
        
        Ok(serde_json::json!(hash_str))
    }
    
    /// JSON path extraction
    async fn jsonpath(&self, args: &[Value]) -> Result<Value> {
        if args.len() < 2 {
            return Err(Error::Internal("Missing JSON data or path".to_string()));
        }
        
        let json_data = &args[0];
        let path = args[1].as_str().unwrap_or("");
        
        // TODO: Implement actual JSONPath extraction
        // This is a placeholder implementation
        Ok(serde_json::json!({
            "path": path,
            "result": json_data
        }))
    }
    
    /// Regular expression match
    async fn regex_match(&self, args: &[Value]) -> Result<Value> {
        if args.len() < 2 {
            return Err(Error::Internal("Missing text or pattern".to_string()));
        }
        
        let text = args[0].as_str().unwrap_or("");
        let pattern = args[1].as_str().unwrap_or("");
        
        match regex::Regex::new(pattern) {
            Ok(re) => {
                let matches: Vec<String> = re.find_iter(text)
                    .map(|m| m.as_str().to_string())
                    .collect();
                
                Ok(serde_json::json!({
                    "matches": matches,
                    "count": matches.len()
                }))
            }
            Err(e) => Err(Error::Internal(format!("Regex error: {}", e))),
        }
    }
}

impl BuiltinPlugin for UtilsPlugin {
    fn name(&self) -> &str {
        "utils"
    }
    
    fn version(&self) -> &str {
        "1.0.0"
    }
    
    fn functions(&self) -> Vec<String> {
        vec![
            "now".to_string(),
            "random_string".to_string(),
            "uuid".to_string(),
            "sleep".to_string(),
            "base64_encode".to_string(),
            "base64_decode".to_string(),
            "md5".to_string(),
            "sha256".to_string(),
            "jsonpath".to_string(),
            "regex_match".to_string(),
        ]
    }
    
    async fn call_function(&self, function_name: &str, args: &[Value]) -> Result<Value> {
        match function_name {
            "now" => self.now(args).await,
            "random_string" => self.random_string(args).await,
            "uuid" => self.uuid(args).await,
            "sleep" => self.sleep(args).await,
            "base64_encode" => self.base64_encode(args).await,
            "base64_decode" => self.base64_decode(args).await,
            "md5" => self.md5(args).await,
            "sha256" => self.sha256(args).await,
            "jsonpath" => self.jsonpath(args).await,
            "regex_match" => self.regex_match(args).await,
            _ => Err(Error::Plugin(crate::error::PluginError::FunctionNotFound {
                function_name: function_name.to_string(),
            })),
        }
    }
    
    async fn initialize(&mut self, config: ValueMap) -> Result<()> {
        self.config = config;
        tracing::info!("Utils plugin initialized");
        Ok(())
    }
    
    async fn cleanup(&mut self) -> Result<()> {
        tracing::info!("Utils plugin cleaned up");
        Ok(())
    }
}

impl Default for UtilsPlugin {
    fn default() -> Self {
        Self::new()
    }
}
